  // ---------------------------------------------------------------
//                    flight005.pde
//                     Jan 15 2009
//                  written by elekid
//
// Test program for self-study
//
// VERSION HISTORY
// V0.4 implementation of drawing path graphics
// V0.5 added making movie function
// 
// Pocessing official site - http://processing.org
// ---------------------------------------------------------------

import processing.video.*;

MovieMaker mm;  // Declare MovieMaker object


// define constants
static final boolean MAKE_MOVIE =false;

static final int FR_SEC = 24;  // frames per second
static final int FR_DAY = 12;  // frames per one day

static final int MAX_PATH = 10;  // control height of arc(travel path)
static final float DRAW_PERCENTAGE = 0.7; // control run/stop timing


static final int TOKYO=0;
static final int KANAZAWA=1;
static final int HIROSHIMA=2;
static final int FUKUOKA=3;

// define grobal variables
PImage MapImage;
PGraphics pg;
PFont labelFont;
String dateText;

// local defined classes
locPoint[]  loc;
Travel[] travel;
Flag[] flags;


int day = 0 ; // count up when dayfremae >= FR_DAY
int pt = 0 ; // counter for array travel
int dayframe = 0 ; // count up each time draw() called and reset when dayfremae >= FR_DAY
int MaxNumTravels = 0 ;
int MaxNumDays = 0 ;

int nt;


// Arrays for store data
int [][] pathCounter={ 
  {
    0,0,0,0    }
  ,{
    0,0,0,0    }
  ,{
    0,0,0,0    }
  ,{
    0,0,0,0    }
};

color [][] pathColor = {
  {
    #000000, #A00000,#008000,#000080 }
    ,{
    #A00000, #000000,#4030C0,#400040 }
    ,{
    #008000, #4030C0,#000000,#30A0C0 }
    ,{
    #000080, #400040,#30A0C0,#000000 }
};    
int [][] pathDirect={ 
  {
    0,1,0,1    }
  ,{
    0,0,1,1    }
  ,{
    1,0,0,1    }
  ,{
    0,0,0,0    }
};

// int [] SumTravelers = { 0,0,0,0};
int [] serviceIn = { 0,0,0,0};

/**
 * called from Processing environment
 */
void setup()
{
  String[] lines;
  size(727,649);
  pg = createGraphics (727,649,P2D);
  pg.beginDraw();
  pg.background(0,0);
  pg.endDraw();

  //smooth();
  //  print(atan2(1,1));

  MapImage = loadImage("JPN.png");

  labelFont=createFont("SansSerif", 20);
  textFont(labelFont);

  loadDataFile("travel_data.txt"); 

  // HND(TOKYO)    505,390
  // KMQ(KOMATSU)  367,342
  // FKU(FUKUOKA)  82,506  
  // HIJ(HIROSIMA)  174,464
  loc = new locPoint[4];
  loc[TOKYO]     = new locPoint(505,390);
  loc[KANAZAWA]  = new locPoint(367,342);
  loc[HIROSHIMA] = new locPoint(174,464);
  loc[FUKUOKA]   = new locPoint(82,506);

  flags = new Flag[4];
  flags[1] = new Flag (loc[1].x,loc[1].y );
  flags[2] = new Flag (loc[2].x,loc[2].y );
  flags[3] = new Flag (loc[3].x,loc[3].y );

  frameRate(FR_SEC);

  // Create MovieMaker object with size, filename,
  // compression codec and quality, framerate
  if (MAKE_MOVIE) {
    mm = new MovieMaker(this, width, height, "drawing.mov",
                        FR_SEC, MovieMaker.CINEPAK, MovieMaker.BEST, 10);
  }

  // DEBUG
  // println("MaxNumTravels" + MaxNumTravels);
  // println("MaxNumDays   " + MaxNumDays);

}

/**
 * called from Processing environment
 */
void draw()
{
  // DEBUG
  // println("day=" + day + ":pt=" + pt + " dayframe=" + dayframe );
  int i;

  image(MapImage, 0, 0);
  flightControl();
  image(pg, 0, 0);

  textSize(36);
  fill(255,0,0);
  text (dateText, 400,600);

  for ( i = 1 ; i <= 3 ; i++) {
    loc[i].showTotal();
  }


  if (day < MaxNumDays) {
    dayframe++;
    if (dayframe >= FR_DAY) {
      day++;
      dayframe = 0 ;
      // DEBUG
      // println("day++=" + day);
      // println("   pt=" + pt);
    }
  }
  
  for ( i = 1 ; i <= 3 ; i++) {
    if ( day >= serviceIn[i]  ) {
      flags[i].show();
    }
  }
  if (MAKE_MOVIE){
    mm.addFrame();
  }
}

void keyPressed() {
  if (key == ' ') {
    if (MAKE_MOVIE){
      mm.finish();  // Finish the movie if space bar is pressed!
    }
  }
}



/**
 * called from draw
 */
void flightControl() {

  // test
  //Travel t = new Travel();
  //t.hasNext = false;
  if ( pt < MaxNumTravels)
    dateText = travel[pt].yy + "/" + travel[pt].mm + "/" + travel[pt].dd + "(" + travel[pt].week + ")";

  if (day >= MaxNumDays) return;
  if ( dayframe ==0 ) {
    // how many object should we move?
    if ( travel[pt].numMoving == 0 ) {
      pt++;
      nt = 0;
    } 
    else {
      nt = 1;
      while ( travel[pt].hasNext ) {
        nt++ ;
        pt++;
      }
      pt++;
    }
    // DEBUG
    // println("travels=" + nt);

    // count up go / back 
    for ( int i = (pt - nt) ; i < pt ; i++){
      pathCounter[travel[i].dst][travel[i].src]++;
      pathCounter[travel[i].src][travel[i].dst]++;
    }
  }

  for ( int i = (pt - nt) ; i < pt ; i++){
    drawPath(travel[i]);
  }

}

/**
 * draw flight path
 */
void drawPath(Travel t){
  // DEBUG
  int x1,x2,y1,y2;
  int midx,midy;
  int lenx,leny;
  float lenc;
  float lenrad;
  float ang;
  float rate;

  float start_ang;
  float end_ang;

  rate = (dayframe + 1.0)/FR_DAY/DRAW_PERCENTAGE;
  if (rate >= 1 ) {  // Arrived at destination
    int m;
    m = t.numMoved(); // Assume that numMoved() always return 0 except when called at first time
    loc[t.src].minus(m);
    loc[t.dst].plus(m);
    return;
  }


  x2 = loc[t.src].x;
  y2 = loc[t.src].y;
  x1 = loc[t.dst].x;
  y1 = loc[t.dst].y;
  if (x1 > x2)  {
    lenx = x1-x2;
    midx = (lenx)/2+x2;
  } 
  else {
    lenx = x2-x1;
    midx = (lenx)/2+x1;
  }
  if (y1 > y2)  {
    leny = y1 - y2;
    midy = (leny)/2 + y2;
  } 
  else {
    leny = y2 - y1;
    midy = (leny)/2 + y1;
  }

  lenc = sqrt(pow(lenx,2) + pow(leny,2));   // get length 
  ang = atan2(y2-y1, x2-x1);                // get angle
  lenrad = lenc/2;                          // get radious (半径)

  // Drawing Arc from source to destination.
  // if pathDirect[][] is 1 then angle of arc start from  2PI to PI, else 0 to PI 
  // Syntax  arc(x, y, width, height, start, stop)
  //         start should be smaller than stop
 
  pg.beginDraw();
  pg.noFill();
  pg.translate(midx,midy);
  pg.rotate(ang);
  pg.strokeWeight(2+ pathCounter[t.src][t.dst]/4);
  //pg.stroke(pathColor[src][dst], 10);
  pg.stroke(pathColor[t.src][t.dst]);
  
  // if ( false ) {
  if ( pathDirect[t.src][t.dst] == 1) {
    //start_ang = PI * 2;
    // end_ang  = PI * (2 - rate );
    start_ang  = PI * (2 - rate );
    end_ang  = PI * 2;

  } else {
    start_ang = 0;
    end_ang = PI * rate;
  }

  pg.arc( 0, 0 , lenc, lenc * pathCounter[t.src][t.dst] / MAX_PATH , start_ang, end_ang);
  pg.endDraw();

  // DEBUG
  // println("--->travel from " + t.src + " to " + t.dst + " step " + step + " (Total:"+pathCounter[src][dst]+" times)"+"rate:"+rate+" start_ang:"+start_ang+" end_ang:"+end_ang);

}



/**
 * read travel data file and store
 */
void loadDataFile(String fileName) {
  int cnt =0,days=0 ; 
  String prev_mm="",prev_dd="";
  int prev_time=0;
  String lines[] = loadStrings(fileName);
  int d;

  travel = new Travel[lines.length];
  for ( int i=0; i < lines.length; i++){
    if (trim(lines[i]).length() == 0) {
      continue;
    } 
    else if ( lines[i].startsWith("#") ) {
      continue;
    }
    String[] pieces = split(lines[i], ",");
    travel[cnt] = new Travel ( pieces[0], pieces[1], pieces[2], 
    pieces[3], pieces[4], pieces[5], pieces[6], pieces[7], 
    pieces[8], pieces[9], pieces[10], pieces[11], pieces[12]);
    
    d = parseInt(pieces[13]);
    if ( d != 0 ) {
      serviceIn[d] = days;
    }

    if (( !prev_mm.equals(travel[cnt].mm) ) || 
      ( !prev_dd.equals(travel[cnt].dd) ) || 
      ( prev_time != travel[cnt].time) )
    {
      days++;
      // DEBUG
      // println("     dd:"+travel[cnt].dd);
      // println("   days:"+days);

    }
    prev_mm=travel[cnt].mm;
    prev_dd=travel[cnt].dd;
    prev_time=travel[cnt].time;

    cnt++;

  }
  MaxNumTravels = cnt;
  MaxNumDays = days;

  // DEBUG
  println("cnt=" + cnt);
  println("days=" + days);
  for (int i =0; i < MaxNumTravels ; i++){
    travel[i].Dump();
  }

  return;

}  
// ---------------------------------------------------------------
//  class locPoint
// 
// ---------------------------------------------------------------
class locPoint {
  int x,y;

  int total_members = 0 ;
  int current_members = 0 ;

  locPoint(int _x, int _y) {
    x = _x;
    y = _y;
  }

  void plus(int x) {
    total_members += x;
    current_members += x;
  }
  void minus(int x ) {
    current_members -= x;
  }
  
  void showTotal() {
    String msg;
    textSize(24);
    fill(255,0,0);
    
    msg =  current_members + " (" + total_members + ")";

    text (msg, x, y -70 );
  }



}

// ---------------------------------------------------------------
//  class Travel
// 
// ---------------------------------------------------------------
class Travel {
  String yy;
  String mm;
  String dd;
  String week;
  int time;
  int src=0;
  int dst=0;
  int numMoving=0;
  int numLoc0=0;
  int numLoc1=0;
  int numLoc2=0;
  int numLoc3=0;
  boolean hasNext=false;
  boolean arrive_flg=false;

  Travel(String _yy, String _mm, String _dd, String _week, String _time, String _src, 
  String _dst, String _numMoving, String _hasNext, 
  String _numLoc0, String _numLoc1, String _numLoc2, String _numLoc3){

    yy = _yy ;
    mm = _mm ;
    dd = _dd ;
    week = _week ;
    time =parseInt(_time) ; 
    src = parseInt(_src);
    dst = parseInt(_dst);
    numMoving = parseInt (_numMoving);
    numLoc0 = parseInt(_numLoc0);
    numLoc1 = parseInt(_numLoc1);
    numLoc2 = parseInt(_numLoc2);
    numLoc3 = parseInt(_numLoc3);

    if ( parseInt(_hasNext) == 1) {
      hasNext=true;
    }
    //println(this.numMoving);

  }
  // FOR DEBUG USE
  void Dump(){
    System.out.printf("%s/%s/%s(%s),time=%d,%d to %d(%d),Loc1=%d,Loc2=%d,Loc3=%d,Loc4=%d\n",
    yy, mm,dd,week,time,src,dst,numMoving,numLoc0,numLoc1,numLoc2,numLoc3);

    return;
  }
  int numMoved(){
    if (arrive_flg == false) {
      arrive_flg=true;
      return numMoving;
    } else {
      return 0;
    }
  }

  
}


// ---------------------------------------------------------------
//  class Flag
// 
// ---------------------------------------------------------------
class Flag {
  static final int numImages = 11;
  int x,y ;
  int flame = 0 ;
  PImage[] images = new PImage[numImages];
  
  Flag ( int _x, int _y){
    String imagefile;

    x = _x;
    y = _y;
    
    for (int i = 0 ; i < numImages ; i++ ) {
      imagefile="flag" + nf(i+1,3)+".gif";
      images[i] = loadImage(imagefile);
    }
    
  }
  void show() {
    image(images[flame++], x -8, y -32 );
    if ( flame >= numImages ) flame = 0 ;
  }
}
